#include "comm/znx_inet.h"
#include "comm/znx_log.h"

char *addr0 = "";                           // error, empty
char *addr1 = "127.0.0.1";
char *addr2 = "127.0.0.1:8080";
char *addr3 = "abcdefgh";                   // error, invalid addr
char *addr4 = "0:0:0:0:0:0:0:0";            // error, non-standard ipv6
char *addr5 = "[0:0:0:0:0:0:0:0]";
char *addr6 = "[0:0:0:0:0:0:0:0]:8080";
char *addr7 = "unix:/tmp/a.sock";
char *addr8 = "unix:/tmp/abcdefghijkl"      // error, long path
    "abcabcdeabcdefghijklabcabcdefghijklmop"
    "qabcdefghijklmopqdefghijklmopqmopqfghij"
    "klmopqabcdefghijklmopqdefghijklmopqmopq";

void test_addr_check()
{
    if (znx_inet_addr_check((u_char *)addr0) != 0) {
        ZNX_DEF_LOG_FATAL("except 0, but got other");
    }

    if (znx_inet_addr_check((u_char *)addr1) != 1) {
        ZNX_DEF_LOG_FATAL("except 1, but got other");
    }

    if (znx_inet_addr_check((u_char *)addr2) != 0) {
        ZNX_DEF_LOG_FATAL("except 0, but got other");
    }

    if (znx_inet_addr_check((u_char *)addr3) != 0) {
        ZNX_DEF_LOG_FATAL("except 0, but got other");
    }

    if (znx_inet_addr_check((u_char *)addr4) != 2) {
        ZNX_DEF_LOG_FATAL("except 2, but got other");
    }

    if (znx_inet_addr_check((u_char *)addr5) != 0) {
        ZNX_DEF_LOG_FATAL("except 0, but got other");
    }
}

void test_address_parser()
{
    int port;
    znx_address_t address;

    if (znx_address_parser(
        (u_char *)addr0, znx_strlen(addr0), &address, &port)) {
        ZNX_DEF_LOG_FATAL("except false, but got true");
    }

    znx_sockaddr_in_t t_addr_in;
    inet_pton(AF_INET, "127.0.0.1", &t_addr_in.sin_addr);

    if (!znx_address_parser(
        (u_char *)addr1, znx_strlen(addr1), &address, &port)) {
        ZNX_DEF_LOG_FATAL("except true, but got false");
    }

    if (address.sockaddr.sa_family != AF_INET || port != 0 ||
        address.sockaddr_in.sin_port != htons(0) ||
        memcmp(&address.sockaddr_in.sin_addr, &t_addr_in.sin_addr,
            sizeof(t_addr_in.sin_addr)) != 0) {
        ZNX_DEF_LOG_FATAL("test failed");
    }

    if (!znx_address_parser(
        (u_char *)addr2, znx_strlen(addr2), &address, &port)) {
        ZNX_DEF_LOG_FATAL("except true, but got false");
    }

    if (address.sockaddr.sa_family != AF_INET || port != 8080 ||
        address.sockaddr_in.sin_port != htons(8080) ||
        memcmp(&address.sockaddr_in.sin_addr, &t_addr_in.sin_addr,
            sizeof(t_addr_in.sin_addr)) != 0) {
        ZNX_DEF_LOG_FATAL("test failed");
    }

    if (znx_address_parser(
        (u_char *)addr3, znx_strlen(addr3), &address, &port)) {
        ZNX_DEF_LOG_FATAL("except false, but got true");
    }

    if (znx_address_parser(
        (u_char *)addr4, znx_strlen(addr4), &address, &port)) {
        ZNX_DEF_LOG_FATAL("except false, but got true");
    }

    znx_sockaddr_in6_t t_addr_in6;
    inet_pton(AF_INET6, "0:0:0:0:0:0:0:0", &t_addr_in6.sin6_addr);

    if (!znx_address_parser(
        (u_char *)addr5, znx_strlen(addr5), &address, &port)) {
        ZNX_DEF_LOG_FATAL("except true, but got false");
    }

    if (address.sockaddr.sa_family != AF_INET6 || port != 0 ||
        address.sockaddr_in6.sin6_port != htons(0) ||
        memcmp(&address.sockaddr_in6.sin6_addr, &t_addr_in6.sin6_addr,
            sizeof(t_addr_in6.sin6_addr)) != 0) {
        ZNX_DEF_LOG_FATAL("test failed");
    }

    if (!znx_address_parser(
        (u_char *)addr6, znx_strlen(addr6), &address, &port)) {
        ZNX_DEF_LOG_FATAL("except true, but got false");
    }

    if (address.sockaddr.sa_family != AF_INET6 || port != 8080 ||
        address.sockaddr_in6.sin6_port != htons(8080) ||
        memcmp(&address.sockaddr_in6.sin6_addr, &t_addr_in6.sin6_addr,
            sizeof(t_addr_in6.sin6_addr)) != 0) {
        ZNX_DEF_LOG_FATAL("test failed");
    }

    if (!znx_address_parser(
        (u_char *)addr7, znx_strlen(addr7), &address, &port)) {
        ZNX_DEF_LOG_FATAL("except true, but got false");
    }

    if (address.sockaddr.sa_family != AF_UNIX ||
        memcmp(address.sockaddr_un.sun_path, "/tmp/a.sock",
            sizeof("/tmp/a.sock")) != 0) {
        ZNX_DEF_LOG_FATAL("test failed");
    }

    if (znx_address_parser(
        (u_char *)addr8, znx_strlen(addr8), &address, &port)) {
        ZNX_DEF_LOG_FATAL("test failed");
    }
}

void test_address_set_port()
{
    znx_address_t address;
    bzero(&address, sizeof(znx_address_t));

    if (znx_address_set_port(&address, 80)) {
        ZNX_DEF_LOG_FATAL("except false, but got true");
    }

    int port;
    if (!znx_address_parser((u_char *)"127.0.0.1",
        strlen("127.0.0.1"), &address, &port) || port != 0) {
        ZNX_DEF_LOG_FATAL("except true, but got false");
    }

    if (!znx_address_set_port(&address, 8080) ||
        address.sockaddr_in.sin_port != htons(8080)) {
        ZNX_DEF_LOG_FATAL("except true, but got false");
    }

    if (!znx_address_parser((u_char *)"[0:0:0:0:0:0:0:0]",
        strlen("[0:0:0:0:0:0:0:0]"), &address, &port) || port != 0) {
        ZNX_DEF_LOG_FATAL("except true, but got false");
    }

    if (!znx_address_set_port(&address, 8080) ||
        address.sockaddr_in6.sin6_port != htons(8080)) {
        ZNX_DEF_LOG_FATAL("except true, but got false");
    }

    if (!znx_address_parser((u_char *)"[0:0:0:0:0:0:0:0]:443",
        strlen("[0:0:0:0:0:0:0:0]:443"), &address, &port) || port == 0) {
        ZNX_DEF_LOG_FATAL("except true, but got false");
    }

    if (!znx_address_set_port(&address, 8080) ||
        address.sockaddr_in6.sin6_port != htons(8080)) {
        ZNX_DEF_LOG_FATAL("except true, but got false");
    }
}

void test_address_other_funs()
{
    int port;
    znx_address_t address;
    if (!znx_address_parser((u_char *)"[0:0:0:0:0:0:0:0]:443",
        strlen("[0:0:0:0:0:0:0:0]:443"), &address, &port)
            || port != 443) {
        ZNX_DEF_LOG_FATAL("except true, but got false");
    }

    znx_address_t new_address;
    bzero(&new_address, sizeof(new_address));
    if (!znx_address_init_by_sockaddr(&new_address, &address.sockaddr) ||
        new_address.sockaddr.sa_family != AF_INET6|| 
        new_address.sockaddr_in6.sin6_port != htons(443) ||
        memcmp(&new_address.sockaddr_in6.sin6_addr,
            &address.sockaddr_in6.sin6_addr,
            sizeof(address.sockaddr_in6.sin6_addr)) != 0) {
        ZNX_DEF_LOG_FATAL("except true, but got false");
    }

    u_char  buf[ZNX_SOCKADDR_SIZE];

    // int64_t n = znx_address_to_str(&address, buf, ZNX_SOCKADDR_SIZE);
    // buf[n] = '\0'; // buf is [::]:443
    // if (strcmp(buf, "[0:0:0:0:0:0:0:0]:443") != 0) {
    //     ZNX_LOG_FATAL("except [0:0:0:0:0:0:0:0]:443, but got %s", buf);
    // }

    bzero(&new_address, sizeof(new_address));
    if (!znx_address_parser((u_char *)"127.0.0.1:80", strlen("127.0.0.1:80"),
        &address, &port) || port != 80) {
        ZNX_DEF_LOG_FATAL("except true, but got false");
    }

    if (!znx_address_init_by_sockaddr(&new_address, &address.sockaddr) ||
        new_address.sockaddr.sa_family != AF_INET ||
        new_address.sockaddr_in.sin_port != htons(80) ||
        memcmp(&new_address.sockaddr_in.sin_addr,
            &address.sockaddr_in.sin_addr,
            sizeof(address.sockaddr_in.sin_addr)) != 0) {
        ZNX_DEF_LOG_FATAL("except true, but got false");
    }

    int64_t n = znx_address_to_str(&address, buf, ZNX_SOCKADDR_SIZE);
    buf[n] = '\0';
    if (znx_strcmp(buf, (u_char *)"127.0.0.1:80") != 0) {
        ZNX_DEF_LOG_FATAL("except 0, but got other");
    }

    bzero(&new_address, sizeof(new_address));
    if (!znx_address_parser((u_char *)"unix:/tmp/a.sock",
        strlen("unix:/tmp/a.sock"), &address, &port)) {
        ZNX_DEF_LOG_FATAL("except true, but got false");
    }

    if (!znx_address_init_by_sockaddr(&new_address, &address.sockaddr)) {
        ZNX_DEF_LOG_FATAL("except true, but got false");
    }

    if (!znx_address_init_by_sockaddr(&new_address, &address.sockaddr) ||
        new_address.sockaddr.sa_family != AF_UNIX ||
        memcmp(&new_address.sockaddr_un.sun_path,
            "/tmp/a.sock", strlen("/tmp/a.sock")) != 0 ||
        *(new_address.sockaddr_un.sun_path + strlen("/tmp/a.sock")) != '\0') {
        ZNX_DEF_LOG_FATAL("except true, but got false");
    }

    n = znx_address_to_str(&address, buf, ZNX_SOCKADDR_SIZE);
    buf[n] = '\0';
    if (znx_strcmp(buf, (u_char *)"unix:/tmp/a.sock") != 0) {
        ZNX_DEF_LOG_FATAL("except 0, but got other");
    }
}

int main() {
    test_addr_check();
    test_address_parser();
    test_address_set_port();
    test_address_other_funs();
    return 0;
}
